package com.danicsoft.daide.command;

import java.io.IOException;

import com.danicsoft.daide.binary.Connection;
import com.danicsoft.daide.binary.DisconnectedException;
import com.danicsoft.daide.binary.StateChangeHandler;
import com.danicsoft.daide.token.TokenGroup;
import com.danicsoft.daide.token.TokenParser;
import com.danicsoft.daide.token.TokenMap.Commands;

/**
 * Any class that is interested in recieving commands must subclass this class.
 * There are abstract methods corresponding to every command that can be
 * received.
 * 
 * @author Daniel Yule (daniel.yule@gmail.com)
 * @version 1.0
 */
public abstract class CommandTarget implements CommandHandler, StateChangeHandler {

	/**
	 * The parser we use for parsing messages.
	 */
	private TokenParser myParser;

	/**
	 * The connection to send and recieve messages from.
	 */
	protected Connection conn;

	/**
	 * Consutrct a new Command Target that will recieve and send commands on
	 * <code>conn</code> and use <code>parser to parse messages.
	 * 
	 * @param conn 
	 * 			The connection to send and receive commands on.
	 * @param parser 
	 * 			The parser to use to parse messages.
	 */
	public CommandTarget(Connection conn, TokenParser parser) {
		this.conn = conn;
		myParser = parser;
		CommandParser cParser = new CommandParser();
		cParser.addHandler(this);
		parser.addMessageListener(cParser);
		conn.addStateChangeHandler(this);

	}

	/**
	 * Accpts a command that we have received (by sending
	 * <code>YES(<em>command</em>)</code>)
	 * 
	 * @param command
	 *        The <code>Command</code> that we are accepting.
	 */
	public void acceptCommand(Command command) {
		sendCommand(new YesCommand(command));
	}

	/**
	 * Informs the player that they created a bad command.
	 * 
	 * @param command
	 *        The command that was not understood
	 */
	public void badCommand(Command command) {
		// TODO Log when a bad command is received.
		Command newCommand = command;
		switch (command.getStatus()) {
			case ACCEPTED:
				newCommand = new YesCommand(command);
				break;
			case REJECTED:
				newCommand = new RejectedCommand(command);
				break;
			case TRANSMITTING:
				newCommand = command;
				break;
		}
		sendCommand(new HuhCommand(newCommand, "Unexpected command"));

	}

	/**
	 * Boot the other end because they send a command that cannot be recovered
	 * from.
	 * 
	 * @param cause
	 *        The command that caused the problem
	 */
	public void bootBecuase(Command cause) {
		badCommand(cause);
		sendCommand(new PissOffCommand());
	}

	public void CommandAccepted(Command command) {
		switch (command.getType()) {
			case Commands.NME:
				AcceptedName((NameCommand) command);
				break;
			case Commands.OBS:
				AcceptedObserver((ObserverCommand) command);
				break;
			case Commands.IAM:
				AcceptedIam((IamCommand) command);
				break;
			case Commands.MAP:
				AcceptedMap((MapCommand) command);
				break;
			case Commands.GOF:
				AcceptedGoFlag((GoFlagCommand) command);
				break;
			case Commands.NOT:
				AcceptedNot((NotCommand) command);
				break;
			case Commands.SVE:
				AcceptedSave((SaveCommand) command);
				break;
			case Commands.TME:
				AcceptedTime((TimeCommand) command);
				break;
			case Commands.DRW:
				AcceptedDraw((DrawCommand) command);
				break;

		}

	}

	/**
	 * Accepted a DrawCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The DrawCommand that has been Accepted.
	 */
	protected abstract void AcceptedDraw(DrawCommand command) ;

	public void CommandError(TokenGroup commandGroup, String parsingProblem) {
		// TODO Log this.
		sendCommand(new HuhCommand(commandGroup, parsingProblem));

	}

	public void CommandReceived(Command command) {
		switch (command.getType()) {
			case Commands.NME:
				ReceivedName((NameCommand) command);
				break;
			case Commands.OBS:
				ReceivedObserver((ObserverCommand) command);
				break;
			case Commands.IAM:
				ReceivedIam((IamCommand) command);
				break;
			case Commands.MAP:
				ReceivedMap((MapCommand) command);
				break;
			case Commands.MDF:
				ReceivedMapDefintion((MapDefinitionCommand) command);
				break;
			case Commands.HLO:
				ReceivedHello((HelloCommand) command);
				break;
			case Commands.SCO:
				ReceivedSupplyOwnership((SupplyOwnershipCommand) command);
				break;
			case Commands.NOW:
				ReceivedNow((NowCommand) command);
				break;
			case Commands.ORD:
				ReceivedOrder((OrderCommand) command);
				break;
			case Commands.OFF:
				ReceivedOff((PissOffCommand) command);
				break;
			case Commands.PRN:
				ReceivedParenthesis((ParenthesisCommand) command);
				break;
			case Commands.SUB:
				ReceivedSubmit((SubmitCommand) command);
				break;
			case Commands.THX:
				ReceivedThanks((ThanksCommand) command);
				break;
			case Commands.MIS:
				ReceivedMissing((MissingCommand) command);
				break;
			// No Accept or Reject implemented after this
			case Commands.ADM:
				ReceivedAdminMessage((AdminMessageCommand) command);
				break;
			case Commands.GOF:
				ReceivedGoFlag((GoFlagCommand) command);
				break;
			case Commands.HST:
				ReceivedHistory((HistoryCommand) command);
				break;
			case Commands.NOT:
				ReceivedNot((NotCommand) command);
				break;
			case Commands.SVE:
				ReceivedSave((SaveCommand) command);
				break;
			case Commands.TME:
				ReceivedTime((TimeCommand) command);
				break;
			case Commands.HUH:
				ReceivedHuh((HuhCommand) command);
				break;
			case Commands.SLO:
				ReceivedSolo((SoloCommand)command);
				break;
			case Commands.DRW:
				ReceivedDraw((DrawCommand) command);
				break;
			case Commands.SMR:
				ReceivedSummary((SummaryCommand) command);
				break;

		}

	}
	
	/**
	 * Received a SummaryCommand. Subclasses must override this method
	 * to react to this command.
	 * 
	 * @param command
	 *        The SummaryCommand that has been received.
	 */
	protected abstract void ReceivedSummary(SummaryCommand command);



	/**
	 * Received a DrawCommand. Subclasses must override this method
	 * to react to this command.
	 * 
	 * @param command
	 *        The DrawCommand that has been received.
	 */
	protected abstract void ReceivedDraw(DrawCommand command) ;

	public void CommandRejected(Command command) {
		switch (command.getType()) {
			case Commands.NME:
				RejectedName((NameCommand) command);
				break;
			case Commands.OBS:
				RejectedObserver((ObserverCommand) command);
				break;
			case Commands.IAM:
				RejectedIam((IamCommand) command);
				break;
			case Commands.MAP:
				RejectedMap((MapCommand) command);
				break;
			case Commands.HLO:
				RejectedHello((HelloCommand) command);
				break;
			case Commands.SCO:
				RejectedSupplyOwnership((SupplyOwnershipCommand) command);
				break;
			case Commands.NOW:
				RejectedNow((NowCommand) command);
				break;
			case Commands.ORD:
				RejectedOrder((OrderCommand) command);
				break;
			case Commands.SUB:
				RejectedSubmit((SubmitCommand) command);
				break;
			case Commands.ADM:
				RejectedAdminMessage((AdminMessageCommand) command);
				break;
			case Commands.GOF:
				RejectedGoFlag((GoFlagCommand) command);
				break;
			case Commands.HST:
				RejectedHistory((HistoryCommand) command);
				break;
			case Commands.NOT:
				RejectedNot((NotCommand) command);
				break;
			case Commands.SVE:
				RejectedSave((SaveCommand) command);
				break;
			case Commands.TME:
				RejectedTime((TimeCommand) command);
				break;
			case Commands.DRW:
				RejectedDraw((DrawCommand) command);

		}

	}
	
	/**
	 * Rejected a DrawCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The DrawCommand that has been Rejected.
	 */
	protected abstract void RejectedDraw(DrawCommand command) ;

	public void connected() {
	// Automatically generated

	}

	/**
	 * Disconnects this connection.
	 */
	public void disconnect() {
		conn.disconnect();
	}

	public void disconnected() {
	// Automatically generated

	}

	public void newConnection(Connection conn1) {
	// Automatically generated

	}

	/**
	 * Rejects a command that we have received (by sending
	 * <code>REJ(<em>command</em>)</code>). Reasons for rejecting are
	 * things like an invalid game state or a nonsense command. If there is an
	 * error is semantics or syntax, a <code>HUH</code> message should be send
	 * instead.
	 * 
	 * @param command
	 *        The <code>Command</code> that we are rejecting.
	 */
	public void rejectCommand(Command command) {
		sendCommand(new RejectedCommand(command));
	}

	/**
	 * Send a command.
	 * 
	 * @param command
	 *        The command to send.
	 */
	public void sendCommand(Command command) {
		try {
			myParser.send(command.output());
		}
		catch (DisconnectedException e) {
			// TODO better logging.
			e.printStackTrace();
		}

	}

	/**
	 * Severs this connection, sending no final message.
	 */
	public void sever() {
		conn.severConnection();
	}

	/**
	 * Accepted a GoFlagCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The GoFlagCommand that has been Accepted.
	 */
	protected abstract void AcceptedGoFlag(GoFlagCommand command);

	/**
	 * Accepted a IamCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The IamCommand that has been Accepted.
	 */
	protected abstract void AcceptedIam(IamCommand command);

	/**
	 * Accepted a LoadCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The LoadCommand that has been Accepted.
	 */
	protected abstract void AcceptedLoad(LoadCommand command);

	/**
	 * Accepted a MapCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The MapCommand that has been Accepted.
	 */
	protected abstract void AcceptedMap(MapCommand command);

	/**
	 * Accepted a NameCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NameCommand that has been Accepted.
	 */
	protected abstract void AcceptedName(NameCommand command);

	/**
	 * Accepted a NotCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NotCommand that has been Accepted.
	 */
	protected abstract void AcceptedNot(NotCommand command);

	/**
	 * Accepted a ObserverCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The ObserverCommand that has been Accepted.
	 */
	protected abstract void AcceptedObserver(ObserverCommand command);

	/**
	 * Accepted a SaveCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The SaveCommand that has been Accepted.
	 */
	protected abstract void AcceptedSave(SaveCommand command);

	/**
	 * Accepted a TimeCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The TimeCommand that has been Accepted.
	 */
	protected abstract void AcceptedTime(TimeCommand command);

	/**
	 * Connect this command target to the parser that it will recieve tokens
	 * from.
	 * 
	 * @throws IOException
	 *         If we could not connect.
	 */
	protected void connect() throws IOException {
		conn.connect(myParser);
	}

	/**
	 * Received a AdminMessageCommand. Subclasses must override this method to
	 * react to this command.
	 * 
	 * @param command
	 *        The AdminMessageCommand that has been received.
	 */
	protected abstract void ReceivedAdminMessage(AdminMessageCommand command);

	/**
	 * Received a GoFlagCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The GoFlagCommand that has been received.
	 */
	protected abstract void ReceivedGoFlag(GoFlagCommand command);

	/**
	 * Received a HelloCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The HelloCommand that has been received.
	 */
	protected abstract void ReceivedHello(HelloCommand command);

	/**
	 * Received a HistoryCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The HistoryCommand that has been received.
	 */
	protected abstract void ReceivedHistory(HistoryCommand command);

	/**
	 * Received a HuhCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The HuhCommand that has been received.
	 */
	protected abstract void ReceivedHuh(HuhCommand command);

	/**
	 * Received a IamCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The IamCommand that has been received.
	 */
	protected abstract void ReceivedIam(IamCommand command);

	/**
	 * Received a MapCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The MapCommand that has been received.
	 */
	protected abstract void ReceivedMap(MapCommand command);

	/**
	 * Received a MapDefinitionCommand. Subclasses must override this method to
	 * react to this command.
	 * 
	 * @param command
	 *        The MapDefinitionCommand that has been received.
	 */
	protected abstract void ReceivedMapDefintion(MapDefinitionCommand command);

	/**
	 * Received a MissingCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The MissingCommand that has been received.
	 */
	protected abstract void ReceivedMissing(MissingCommand command);

	/**
	 * Received a NameCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NameCommand that has been received.
	 */
	protected abstract void ReceivedName(NameCommand command);

	/**
	 * Received a NotCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NotCommand that has been received.
	 */
	protected abstract void ReceivedNot(NotCommand command);

	/**
	 * Received a NowCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NowCommand that has been received.
	 */
	protected abstract void ReceivedNow(NowCommand command);

	/**
	 * Received a ObserverCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The ObserverCommand that has been received.
	 */
	protected abstract void ReceivedObserver(ObserverCommand command);

	/**
	 * Received a PissOffCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The PissOffCommand that has been received.
	 */
	protected abstract void ReceivedOff(PissOffCommand command);

	/**
	 * Received a OrderCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The OrderCommand that has been received.
	 */
	protected abstract void ReceivedOrder(OrderCommand command);

	/**
	 * Received a ParenthesisCommand. Subclasses must override this method to
	 * react to this command.
	 * 
	 * @param command
	 *        The ParenthesisCommand that has been received.
	 */
	protected abstract void ReceivedParenthesis(ParenthesisCommand command);

	/**
	 * Received a SaveCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The SaveCommand that has been received.
	 */
	protected abstract void ReceivedSave(SaveCommand command);

	/**
	 * Received a SoloCommand.  Subclasses must override this method to react to this command
	 * @param command The SoloCommand that has been received.
	 */
	protected abstract void ReceivedSolo(SoloCommand command);

	/**
	 * Received a SubmitCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The SubmitCommand that has been received.
	 */
	protected abstract void ReceivedSubmit(SubmitCommand command);

	/**
	 * Received a SupplyOwnershipCommand. Subclasses must override this method
	 * to react to this command.
	 * 
	 * @param command
	 *        The SupplyOwnershipCommand that has been received.
	 */
	protected abstract void ReceivedSupplyOwnership(SupplyOwnershipCommand command);

	/**
	 * Received a ThanksCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The ThanksCommand that has been received.
	 */
	protected abstract void ReceivedThanks(ThanksCommand command);

	/**
	 * Received a TimeCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The TimeCommand that has been received.
	 */
	protected abstract void ReceivedTime(TimeCommand command);

	/**
	 * Rejected a AdminMessageCommand. Subclasses must override this method to
	 * react to this command.
	 * 
	 * @param command
	 *        The AdminMessageCommand that has been Rejected.
	 */
	protected abstract void RejectedAdminMessage(AdminMessageCommand command);

	/**
	 * Rejected a GoFlagCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The GoFlagCommand that has been Rejected.
	 */
	protected abstract void RejectedGoFlag(GoFlagCommand command);

	/**
	 * Rejected a HelloCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The HelloCommand that has been Rejected.
	 */
	protected abstract void RejectedHello(HelloCommand command);

	/**
	 * Rejected a HistoryCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The HistoryCommand that has been Rejected.
	 */
	protected abstract void RejectedHistory(HistoryCommand command);

	/**
	 * Rejected a IamCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The IamCommand that has been Rejected.
	 */
	protected abstract void RejectedIam(IamCommand command);

	/**
	 * Rejected a MapCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The MapCommand that has been Rejected.
	 */
	protected abstract void RejectedMap(MapCommand command);

	/**
	 * Rejected a NameCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NameCommand that has been Rejected.
	 */
	protected abstract void RejectedName(NameCommand command);

	/**
	 * Rejected a NotCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NotCommand that has been Rejected.
	 */
	protected abstract void RejectedNot(NotCommand command);

	/**
	 * Rejected a NowCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The NowCommand that has been Rejected.
	 */
	protected abstract void RejectedNow(NowCommand command);

	/**
	 * Rejected a ObserverCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The ObserverCommand that has been Rejected.
	 */
	protected abstract void RejectedObserver(ObserverCommand command);

	/**
	 * Rejected a OrderCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The OrderCommand that has been Rejected.
	 */
	protected abstract void RejectedOrder(OrderCommand command);

	/**
	 * Rejected a SaveCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The SaveCommand that has been Rejected.
	 */
	protected abstract void RejectedSave(SaveCommand command);

	/**
	 * Rejected a SubmitCommand. Subclasses must override this method to react
	 * to this command.
	 * 
	 * @param command
	 *        The SubmitCommand that has been Rejected.
	 */
	protected abstract void RejectedSubmit(SubmitCommand command);

	/**
	 * Rejected a SupplyOwnershipCommand. Subclasses must override this method
	 * to react to this command.
	 * 
	 * @param command
	 *        The SupplyOwnershipCommand that has been Rejected.
	 */
	protected abstract void RejectedSupplyOwnership(SupplyOwnershipCommand command);

	/**
	 * Rejected a TimeCommand. Subclasses must override this method to react to
	 * this command.
	 * 
	 * @param command
	 *        The TimeCommand that has been Rejected.
	 */
	protected abstract void RejectedTime(TimeCommand command);
}
